#pragma once

#include <qlib/all.h>
#include <entt/entt.hpp>

#include "../ecs/query.hpp"

#include "../layout/layout_cmd.hpp"

namespace qbe::utils {

entt::entity copy(entt::registry& reg, entt::entity entity, layout::LayoutCmd& cmd, qlib::Vec2d translate);
entt::entity copy(entt::registry& src_reg, entt::registry& new_reg, entt::entity entity, layout::LayoutCmd& cmd, qlib::Vec2d translate,
                  qlib::Library::s_ptr new_lib = nullptr);

void copy(entt::registry& reg, entt::entity entity, entt::registry& new_reg);

void translate(entt::registry& reg, entt::entity entity, qlib::Vec2d vec);
void rotation(entt::registry& reg, entt::entity entity, qlib::Vec2d center, double rotation);
void rotation(entt::registry& reg, entt::entity entity, double rotation);

void mirror(entt::registry& reg, entt::entity entity, qlib::Vec2d p1, qlib::Vec2d p2);

qlib::Vec2dArray getInitBox();
qlib::Vec2dArray getBox(entt::registry& reg, entt::entity e);
qlib::Vec2dArray boxAndBox(qlib::Vec2dArray& box1, qlib::Vec2dArray& box2);

bool getBox(entt::registry& reg, entt::entity e, qlib::Vec2dArray& res);

void swap(entt::registry& reg, entt::entity a, entt::entity b);

template <typename PtrType>
struct EntitysAndPtrs {
  std::vector<entt::entity> es;
  std::vector<PtrType> ptrs;
};

struct CoreMap {
  EntitysAndPtrs<qlib::Polygon::s_ptr> polygons;
  EntitysAndPtrs<qlib::Path::s_ptr> paths;
  EntitysAndPtrs<qlib::Circle::s_ptr> circles;
  EntitysAndPtrs<qlib::Rectangle::s_ptr> rectangles;
  EntitysAndPtrs<qlib::Ellipse::s_ptr> ellipses;
  EntitysAndPtrs<qlib::Fan::s_ptr> fans;
  EntitysAndPtrs<qlib::Label::s_ptr> labels;
  EntitysAndPtrs<qlib::Reference::s_ptr> refers;
  EntitysAndPtrs<qlib::Ruler::s_ptr> rulers;

  void init(entt::entity e);

  void init(std::vector<entt::entity> es);
};

qlib::Vec2d getAngleAdsorbedPoint(qlib::Vec2d a, qlib::Vec2d b);

qlib::Vec2d getAdsorbedPoint(ecs::Query query);

qlib::Vec2dArray getPartPoints(entt::registry& reg, entt::entity e, qlib::GeoPartType type, uint32_t id);

uint32_t getLayer(entt::registry& reg, entt::entity e);

uint32_t valid_entity_num(entt::registry& reg);
bool entity_valid(entt::registry& reg, entt::entity e);

template <typename T, typename... Args>
void copy_impl(qlib::Cell::s_ptr src_cell, entt::entity src_e, qlib::Cell::s_ptr new_cell) {
  auto& reg = src_cell->reg;
  auto ptr = reg.try_get<T>(src_e);

  if (ptr) {
    auto new_ptr = (*ptr)->copy();
    new_cell->spawn<T>(new_ptr);
    return;
  }

  if constexpr (sizeof...(Args) > 0) {
    return copy_impl<Args...>(src_cell, src_e, new_cell);
  }
}

inline void copy(qlib::Cell::s_ptr src_cell, entt::entity src_e, qlib::Cell::s_ptr new_cell) {
  if (entity_valid(src_cell->reg, src_e)) {
    copy_impl<QLIB_CELL_ELEMENT_PTR_TYPE>(src_cell, src_e, new_cell);
  }
}

template <typename Tag>
void copy(qlib::Cell::s_ptr src_cell, qlib::Cell::s_ptr new_cell) {
  auto view_src = src_cell->reg.view<Tag>();
  view_src.each([&src_cell, &new_cell](entt::entity e) { copy(src_cell, e, new_cell); });
}

template <typename TagType>
void remove_invalid_entity(entt::registry& reg) {
  auto view = reg.view<TagType>();
  view.each([&reg](entt::entity e) {
    if (!entity_valid(reg, e)) {
      reg.remove<TagType>(e);
    }
  });
}

qlib::PolygonPtrArray getPolygons(entt::registry& reg, entt::entity e);

}  // namespace qbe::utils
